/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package assignment1;

import java.io.*;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;

/**
 *
 * @author cjaramillo
 */
public class Rover {
    private int m_xPosition;
    private int m_yPosition;    

    public enum Orientation{MIN_ORIENTATION,UP,UP_RIGHT,RIGHT,DOWN_RIGHT,DOWN,DOWN_LEFT,LEFT,UP_LEFT,MAX_ORIENTATION};
    private Orientation m_orientation;
    private Board m_fullTerrainBoard;
    private Board m_discoveredTerrainBoard;
    private int m_completedMoves;
    private String m_log;
    private boolean m_hasSubgoals;
    private Vector<BoardPosition>m_subgoals;
    private Vector<BoardPosition>m_visitedSubgoals;
    
    Rover()
    {
        m_xPosition = 0;
        m_yPosition = 0;
        m_orientation = Orientation.DOWN;
        m_completedMoves = 0;
        m_log = "";
        m_hasSubgoals = false;
        m_subgoals = new Vector<BoardPosition>();
        m_visitedSubgoals = new Vector<BoardPosition>();
    }
    
    public boolean hasSubgoals()
    {
        return m_hasSubgoals;
    }
    
    public void createSubgoalList()
    {
        int targetColumn = m_discoveredTerrainBoard.targetColumn();
        int targetRow = m_discoveredTerrainBoard.targetRow();
        
        addUpSubgoal(targetRow,targetColumn);
        addUpRightSubgoal(targetRow,targetColumn);
        addRightSubgoal(targetRow,targetColumn);
        addDownRightSubgoal(targetRow,targetColumn);
        addDownSubgoal(targetRow,targetColumn);
        addDownLeftSubgoal(targetRow,targetColumn);
        addLeftSubgoal(targetRow,targetColumn);
        addUpLeftSubgoal(targetRow,targetColumn);
        
        m_hasSubgoals = true;
    }
    
    private void addUpSubgoal(int targetRow, int targetColumn)
    {
        int subgoalColumn = targetColumn;
        int subgoalRow = targetRow - 1;
        if((subgoalRow>=0)&&(subgoalRow<m_discoveredTerrainBoard.rows())&&(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) != Cell.State.OBSTACLE))
        {
            BoardPosition newPosition = new BoardPosition();
            newPosition.column(subgoalColumn);
            newPosition.row(subgoalRow);
            m_subgoals.add(newPosition);
        }
    }
    
    private void addUpRightSubgoal(int targetRow, int targetColumn)
    {
        int subgoalColumn = targetColumn + 1;
        int subgoalRow = targetRow - 1;
        if((subgoalColumn>=0 && subgoalRow>=0)&&(subgoalColumn<m_discoveredTerrainBoard.columns() && subgoalRow<m_discoveredTerrainBoard.rows())&&(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) != Cell.State.OBSTACLE))
        {
            BoardPosition newPosition = new BoardPosition();
            newPosition.column(subgoalColumn);
            newPosition.row(subgoalRow);
            m_subgoals.add(newPosition);
        }
    }
    
    private void addRightSubgoal(int targetRow, int targetColumn)
    {
        int subgoalColumn = targetColumn + 1;
        int subgoalRow = targetRow;
        if((subgoalColumn>=0 && subgoalRow>=0)&&(subgoalColumn<m_discoveredTerrainBoard.columns() && subgoalRow<m_discoveredTerrainBoard.rows())&&(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) != Cell.State.OBSTACLE))
        {
            BoardPosition newPosition = new BoardPosition();
            newPosition.column(subgoalColumn);
            newPosition.row(subgoalRow);
            m_subgoals.add(newPosition);
        }
    }
    
    private void addDownRightSubgoal(int targetRow, int targetColumn)
    {
        int subgoalColumn = targetColumn + 1;
        int subgoalRow = targetRow + 1;
        if((subgoalColumn>=0 && subgoalRow>=0)&&(subgoalColumn<m_discoveredTerrainBoard.columns() && subgoalRow<m_discoveredTerrainBoard.rows())&&(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) != Cell.State.OBSTACLE))
        {
            BoardPosition newPosition = new BoardPosition();
            newPosition.column(subgoalColumn);
            newPosition.row(subgoalRow);
            m_subgoals.add(newPosition);
        }
    }
    
    private void addDownSubgoal(int targetRow, int targetColumn)
    {
        int subgoalColumn = targetColumn;
        int subgoalRow = targetRow + 1;
        if((subgoalColumn>=0 && subgoalRow>=0)&&(subgoalColumn<m_discoveredTerrainBoard.columns() && subgoalRow<m_discoveredTerrainBoard.rows())&&(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) != Cell.State.OBSTACLE))
        {
            BoardPosition newPosition = new BoardPosition();
            newPosition.column(subgoalColumn);
            newPosition.row(subgoalRow);
            m_subgoals.add(newPosition);
        }
    }
    
    private void addDownLeftSubgoal(int targetRow, int targetColumn)
    {
        int subgoalColumn = targetColumn - 1;
        int subgoalRow = targetRow + 1;
        if((subgoalColumn>=0 && subgoalRow>=0)&&(subgoalColumn<m_discoveredTerrainBoard.columns() && subgoalRow<m_discoveredTerrainBoard.rows())&&(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) != Cell.State.OBSTACLE))
        {
            BoardPosition newPosition = new BoardPosition();
            newPosition.column(subgoalColumn);
            newPosition.row(subgoalRow);
            m_subgoals.add(newPosition);
        }
    }
    
    private void addLeftSubgoal(int targetRow, int targetColumn)
    {
        int subgoalColumn = targetColumn - 1;
        int subgoalRow = targetRow;
        if((subgoalColumn>=0 && subgoalRow>=0)&&(subgoalColumn<m_discoveredTerrainBoard.columns() && subgoalRow<m_discoveredTerrainBoard.rows())&&(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) != Cell.State.OBSTACLE))
        {
            BoardPosition newPosition = new BoardPosition();
            newPosition.column(subgoalColumn);
            newPosition.row(subgoalRow);
            m_subgoals.add(newPosition);
        }
    }
    
    private void addUpLeftSubgoal(int targetRow, int targetColumn)
    {
        int subgoalColumn = targetColumn - 1;
        int subgoalRow = targetRow - 1;
        if((subgoalColumn>=0 && subgoalRow>=0)&&(subgoalColumn<m_discoveredTerrainBoard.columns() && subgoalRow<m_discoveredTerrainBoard.rows())&&(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) != Cell.State.OBSTACLE))
        {
            BoardPosition newPosition = new BoardPosition();
            newPosition.column(subgoalColumn);
            newPosition.row(subgoalRow);
            m_subgoals.add(newPosition);
        }
    }
    
    public boolean subgoalsContainObstacles()
    {
        boolean retVal = false;
        for(int i=0; i<m_subgoals.size(); i++)
        {
            int subgoalRow = m_subgoals.elementAt(i).row();
            int subgoalColumn = m_subgoals.elementAt(i).column();
            if(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) == Cell.State.OBSTACLE)
            {
                retVal = true;
                break;
            }
        }
        return retVal;
    }
    
    public void removeObstaclesFromSubgoals()
    {
        for(int i=0; i<m_subgoals.size(); i++)
        {
            int subgoalRow = m_subgoals.elementAt(i).row();
            int subgoalColumn = m_subgoals.elementAt(i).column();
            if(m_discoveredTerrainBoard.cellState(subgoalRow, subgoalColumn) == Cell.State.OBSTACLE)
            {
                m_subgoals.remove(i);
            }
        }
    }
    
    public boolean subgoalsContainCurrentPosition()
    {
        boolean retVal = false;
        for(int i=0; i<m_subgoals.size(); i++)
        {
            int subgoalRow = m_subgoals.elementAt(i).row();
            int subgoalColumn = m_subgoals.elementAt(i).column();
            if(subgoalRow == this.y() && subgoalColumn == this.x())
            {
                retVal = true;
                break;
            }
        }
        return retVal;
    }
    
    public void removePositionFromSubgoals()
    {
        for(int i=0; i<m_subgoals.size(); i++)
        {
            int subgoalRow = m_subgoals.elementAt(i).row();
            int subgoalColumn = m_subgoals.elementAt(i).column();
            if(subgoalRow == this.y() && subgoalColumn == this.x())
            {
                m_visitedSubgoals.add(m_subgoals.elementAt(i));
                m_subgoals.remove(i);
            }
        }
    }
    
    public boolean canReachSubgoal()
    {
        boolean retVal = false;
        int originalTargetRow = m_discoveredTerrainBoard.targetRow();
        int originalTargetColumn = m_discoveredTerrainBoard.targetColumn();
        Cell.State originalCellState;
        for(int i=0; i<m_subgoals.size(); i++)
        {
            BoardPosition subGoalPosition = m_subgoals.elementAt(i);
            originalCellState = m_discoveredTerrainBoard.cellState(subGoalPosition.row(), subGoalPosition.column());
            m_discoveredTerrainBoard.setTarget(subGoalPosition.row(), subGoalPosition.column());
            if(this.awareOfClearPath())
            {
                retVal = true;
                m_discoveredTerrainBoard.setCell(subGoalPosition.row(), subGoalPosition.column(), originalCellState);
                break;
            }
            else
            {
                m_discoveredTerrainBoard.setCell(subGoalPosition.row(), subGoalPosition.column(), originalCellState);
            }
        }
        m_discoveredTerrainBoard.setTarget(originalTargetRow, originalTargetColumn);
        return retVal;
    }
    
    public void followPathToSubgoal()
    {
        int originalTargetRow = m_discoveredTerrainBoard.targetRow();
        int originalTargetColumn = m_discoveredTerrainBoard.targetColumn();
        Cell.State originalCellState;
        for(int i=0; i<m_subgoals.size(); i++)
        {
            BoardPosition subGoalPosition = m_subgoals.elementAt(i);
            originalCellState = m_discoveredTerrainBoard.cellState(subGoalPosition.row(), subGoalPosition.column());
            m_discoveredTerrainBoard.setTarget(subGoalPosition.row(), subGoalPosition.column());
            if(this.awareOfClearPath())
            {
                this.followPathToTarget();
                m_discoveredTerrainBoard.setCell(subGoalPosition.row(), subGoalPosition.column(), originalCellState);
                break;
            }
            else
            {
                m_discoveredTerrainBoard.setCell(subGoalPosition.row(), subGoalPosition.column(), originalCellState);
            }
        }
        m_discoveredTerrainBoard.setTarget(originalTargetRow, originalTargetColumn);
    }
    
    public int completedMoves()
    {
        return m_completedMoves;
    }
    
    public boolean canAddSubgoal()
    {
        boolean retVal = false;
        int maxColumn = m_discoveredTerrainBoard.columns();
        int maxRow = m_discoveredTerrainBoard.rows();
        
        Vector<BoardPosition>subgoalsToAdd = new Vector<BoardPosition>();
        for(int i=0; i< m_subgoals.size(); i++)
        {
            int column = m_subgoals.elementAt(i).column();
            int row = m_subgoals.elementAt(i).row();
            Vector<BoardPosition>potentialSubgoals = new Vector<BoardPosition>();
            BoardPosition upPosition = new BoardPosition(row-1, column);
            potentialSubgoals.add(upPosition);
            BoardPosition upRightPosition = new BoardPosition(row-1, column+1);
            potentialSubgoals.add(upRightPosition);
            BoardPosition rightPosition = new BoardPosition(row, column+1);
            potentialSubgoals.add(rightPosition);
            BoardPosition downRightPosition = new BoardPosition(row+1, column+1);
            potentialSubgoals.add(downRightPosition);
            BoardPosition downPosition = new BoardPosition(row+1, column);
            potentialSubgoals.add(downPosition);
            BoardPosition downLeftPosition = new BoardPosition(row+1, column-1);
            potentialSubgoals.add(downLeftPosition);
            BoardPosition leftPosition = new BoardPosition(row, column-1);
            potentialSubgoals.add(leftPosition);
            BoardPosition upLeftPosition = new BoardPosition(row-1, column-1);
            potentialSubgoals.add(upLeftPosition);
            for(int j=0; j<potentialSubgoals.size();j++)
            {
                if(potentialSubgoals.elementAt(j).row()==-1 || potentialSubgoals.elementAt(j).column()==-1)//off the board on top or to the left
                {
                    potentialSubgoals.remove(j);
                }
                else if(potentialSubgoals.elementAt(j).row() >= maxRow || potentialSubgoals.elementAt(j).column() >= maxColumn)
                {
                    potentialSubgoals.remove(j);
                }
                else if(m_discoveredTerrainBoard.cellState(potentialSubgoals.elementAt(j).row(), potentialSubgoals.elementAt(j).column())==Cell.State.OBSTACLE)
                {
                    potentialSubgoals.remove(j);
                }
                else if(this.isReachedSubgoal(potentialSubgoals.elementAt(j)))
                {
                    potentialSubgoals.remove(j);
                }
                else if(this.isExistingSubgoal(potentialSubgoals.elementAt(j)))
                {
                    potentialSubgoals.remove(j);
                }
                else
                {
                    subgoalsToAdd.add(potentialSubgoals.elementAt(j));
                }
            }
        }
        if(subgoalsToAdd.size() > 0)
        {
            retVal=true;
        }
        for(int x=0; x< subgoalsToAdd.size(); x++)
        {
            m_subgoals.add(subgoalsToAdd.elementAt(x));
        }
        HashSet<BoardPosition> tempSet = new HashSet();
        for(int y=0; y<m_subgoals.size();y++)
        {
            tempSet.add(m_subgoals.elementAt(y));
        }
        m_subgoals = new Vector<BoardPosition>();
        Iterator<BoardPosition> it= tempSet.iterator();
        while(it.hasNext())
        {
            m_subgoals.add(it.next());
        }
        
        return retVal;
    }
    
    private boolean isReachedSubgoal(BoardPosition position)
    {
        boolean retVal = false;
        for(int i = 0; i < m_visitedSubgoals.size(); i++)
        {
            if(m_visitedSubgoals.elementAt(i).equals(position))
            {
                retVal = true;
                break;
            }
        }
        return retVal;
    }
    
    private boolean isExistingSubgoal(BoardPosition position)
    {
        boolean retVal = false;
        for(int i = 0; i < m_subgoals.size(); i++)
        {
            if(m_subgoals.elementAt(i).equals(position))
            {
                retVal = true;
                break;
            }
        }
        return retVal;
    }
    
    public String roverLog()
    {
        return m_log;
    }
    
    public boolean awareOfClearPath() 
    {
        boolean retVal = false;
        int columnDiff = m_xPosition - m_discoveredTerrainBoard.targetColumn();
        int rowDiff = m_yPosition - m_discoveredTerrainBoard.targetRow();
        
        if(columnDiff == 0 || rowDiff == 0 || Math.abs(columnDiff) == Math.abs(rowDiff)) //target is in a straight line from us
        {
            Orientation targetOrientation = targetDirection();
            if(this.isStraightPathClear(targetOrientation, m_discoveredTerrainBoard.targetRow(), m_discoveredTerrainBoard.targetColumn()))
            {
                return true;
            }
        }
        return retVal;
    }
    
    boolean isStraightPathClear(Orientation direction, int row, int column)
    {
        boolean retVal = false;
        switch(direction)
        {
            case UP:
                retVal = this.isPathUpClear(row, column);
                break;
            case UP_RIGHT:
                retVal = this.isPathUpRightClear(row, column);
                break;
            case RIGHT:
                retVal = this.isPathRightClear(row, column);
                break;
            case DOWN_RIGHT:
                retVal = this.isPathDownRightClear(row, column);
                break;
            case DOWN:
                retVal = this.isPathDownClear(row, column);
                break;
            case DOWN_LEFT:
                retVal = this.isPathDownLeftClear(row, column);
                break;
            case LEFT:
                retVal = this.isPathLeftClear(row, column);
                break;
            case UP_LEFT:
                retVal = this.isPathUpLeftClear(row, column);
                break;
        }
        return retVal;
    }
    
    private boolean isPathUpClear(int row, int column)
    {
        boolean pathClear = true;
        int yPosition = m_yPosition-1;
        while(yPosition > row && pathClear)
        {
            if(m_discoveredTerrainBoard.cellState(yPosition, m_xPosition) != Cell.State.CLEAR)
            {
                pathClear = false;
            }
            yPosition--;
        }
        return pathClear;
    }
    
    private boolean isPathDownClear(int row, int column)
    {
        boolean pathClear = true;
        int yPosition = m_yPosition+1;
        while(yPosition < row && pathClear)
        {
            if(m_discoveredTerrainBoard.cellState(yPosition, m_xPosition) != Cell.State.CLEAR)
            {
                pathClear = false;
            }
            yPosition++;
        }
        return pathClear;
    }
    
    private boolean isPathUpRightClear(int row, int column)
    {
        boolean pathClear = true;
        int yPosition = m_yPosition-1;
        int xPosition = m_xPosition+1;
        while(yPosition > row && xPosition < column && pathClear)
        {
            if(m_discoveredTerrainBoard.cellState(yPosition, xPosition) != Cell.State.CLEAR)
            {
                pathClear = false;
            }
            yPosition--;
            xPosition++;
        }
        return pathClear;
    }
    
    private boolean isPathUpLeftClear(int row, int column)
    {
        boolean pathClear = true;
        int yPosition = m_yPosition-1;
        int xPosition = m_xPosition-1;
        while(yPosition > row && xPosition > column && pathClear)
        {
            if(m_discoveredTerrainBoard.cellState(yPosition, xPosition) != Cell.State.CLEAR)
            {
                pathClear = false;
            }
            yPosition--;
            xPosition--;
        }
        return pathClear;
    }
    
    private boolean isPathDownLeftClear(int row, int column)
    {
        boolean pathClear = true;
        int yPosition = m_yPosition+1;
        int xPosition = m_xPosition-1;
        while(yPosition < row && column > 0 && pathClear)
        {
            if(m_discoveredTerrainBoard.cellState(yPosition, xPosition) != Cell.State.CLEAR)
            {
                pathClear = false;
            }
            yPosition++;
            xPosition--;
        }
        return pathClear;
    }
    
    private boolean isPathDownRightClear(int row, int column)
    {
        boolean pathClear = true;
        int yPosition = m_yPosition+1;
        int xPosition = m_xPosition+1;
        while(yPosition < row && xPosition < column && pathClear)
        {
            if(m_discoveredTerrainBoard.cellState(yPosition, xPosition) != Cell.State.CLEAR)
            {
                pathClear = false;
            }
            yPosition++;
            xPosition++;
        }
        return pathClear;
    }
    
    private boolean isPathRightClear(int row, int column)
    {
        boolean pathClear = true;
        int xPosition = m_xPosition+1;
        while(xPosition < column && pathClear)
        {
            if(m_discoveredTerrainBoard.cellState(m_yPosition, xPosition) != Cell.State.CLEAR)
            {
                pathClear = false;
            }
            xPosition++;
        }
        return pathClear;
    }
    
    private boolean isPathLeftClear(int row, int column)
    {
        boolean pathClear = true;
        int xPosition = m_xPosition-1;
        while(xPosition > column && pathClear)
        {
            if(m_discoveredTerrainBoard.cellState(m_yPosition, xPosition) != Cell.State.CLEAR)
            {
                pathClear = false;
            }
            xPosition--;
        }
        return pathClear;
    }
    
    public void followPathToTarget()
    {
        Orientation targetOrientation = targetDirection();
        if(isStraightPathClear(targetOrientation, m_discoveredTerrainBoard.targetRow(), m_discoveredTerrainBoard.targetColumn()))
        {
            followStraightPath();
        }
    }
    
    public void followStraightPath()
    {
        this.orientRoverTowardTarget();
        while(m_xPosition != m_discoveredTerrainBoard.targetColumn() || m_yPosition!=m_discoveredTerrainBoard.targetRow())
        {
            this.moveForward();
        }
    }
    
    public String orientRoverTowardTarget()
    {
        Orientation targetOrientation = targetDirection();
        if(targetOrientation != m_orientation)
        {
            if(m_orientation.ordinal() > targetOrientation.ordinal())
            {
                rotateCounterclockwise(m_orientation.ordinal() - targetOrientation.ordinal());
            }
            else if(m_orientation.ordinal() < targetOrientation.ordinal())
            {
                rotateClockwise(targetOrientation.ordinal() - m_orientation.ordinal());
            }
        }
        return "";
    }
    
    public Orientation targetDirection()
    {
        Orientation targetOrientation = Orientation.MIN_ORIENTATION;
        if(m_discoveredTerrainBoard.targetRow() < m_yPosition && 
                m_discoveredTerrainBoard.targetColumn() == m_xPosition)
        {
            targetOrientation = Orientation.UP;
        }
        if(m_discoveredTerrainBoard.targetRow() > m_yPosition && 
                m_discoveredTerrainBoard.targetColumn() == m_xPosition)
        {
            targetOrientation = Orientation.DOWN;
        }
        if(m_discoveredTerrainBoard.targetRow() == m_yPosition && 
                m_discoveredTerrainBoard.targetColumn() < m_xPosition)
        {
            targetOrientation = Orientation.LEFT;
        }
        if(m_discoveredTerrainBoard.targetRow() == m_yPosition && 
                m_discoveredTerrainBoard.targetColumn() > m_xPosition)
        {
            targetOrientation = Orientation.RIGHT;
        }
        if(m_discoveredTerrainBoard.targetRow() > m_yPosition && 
                m_discoveredTerrainBoard.targetColumn() > m_xPosition)
        {
            targetOrientation = Orientation.DOWN_RIGHT;
        }
        if(m_discoveredTerrainBoard.targetRow() > m_yPosition && 
                m_discoveredTerrainBoard.targetColumn() < m_xPosition)
        {
            targetOrientation = Orientation.DOWN_LEFT;
        }
        if(m_discoveredTerrainBoard.targetRow() < m_yPosition && 
                m_discoveredTerrainBoard.targetColumn() < m_xPosition)
        {
            targetOrientation = Orientation.UP_LEFT;
        }
        if(m_discoveredTerrainBoard.targetRow() < m_yPosition && 
                m_discoveredTerrainBoard.targetColumn() > m_xPosition)
        {
            targetOrientation = Orientation.UP_RIGHT;
        }
        return targetOrientation;
    }
    
    private void moveUp() {
        m_yPosition--;
    }
    
    private void moveUpRight() {
        m_yPosition--;
        m_xPosition++;
    }
    
    private void moveRight() {
        m_xPosition++;
    }

    private void moveDownRight() {
        m_yPosition++;
        m_xPosition++;
    }

    private void moveDown() {        
        m_yPosition++;
    }

    private void moveDownLeft() {
        m_yPosition++;
        m_xPosition--;
    }

    private void moveLeft() {
        m_xPosition--;
    }

    private void moveUpLeft() {
        m_yPosition--;
        m_xPosition--;
    }
    
    private void rotateClockwise(int rotations)
    {
        for (int i = 0; i<rotations; i++)
        {
            this.rotateClockwise();
            this.readSensors();
        }
    }
    
    private void rotateCounterclockwise(int rotations)
    {
        for (int i = 0; i<rotations; i++)
        {
            this.rotateCounterclockwise();
            this.readSensors();
        }
    }
    
    public String rotateClockwise()
    {
        String move = "\nRotate Clockwise: \n\tPrevious Position:("+ m_xPosition + ", " + m_yPosition + ") Previous Orientation: " + m_orientation;
        Orientation newOrientation = Orientation.values()[this.orientation().ordinal()+1];
        if(newOrientation == Orientation.MAX_ORIENTATION)
        {
            newOrientation = Orientation.UP;
        }
        this.orientation(newOrientation);
        move += "\n\tCurrent  Position:("+ m_xPosition + ", " + m_yPosition + ") Current  Orientation: " + m_orientation;
        m_log += move;
        m_completedMoves++;
        return move;
    }
    
    public String rotateCounterclockwise()
    {
        String move = "\nRotate Counterclockwise: \n\tPrevious Position:("+ m_xPosition + ", " + m_yPosition + ") Previous Orientation: " + m_orientation;
        Orientation newOrientation = Orientation.values()[this.orientation().ordinal()-1];
        if(newOrientation == Orientation.MIN_ORIENTATION)
        {
            newOrientation = Orientation.UP_LEFT;
        }
        this.orientation(newOrientation);
        move += "\n\tCurrent  Position:("+ m_xPosition + ", " + m_yPosition + ") Current  Orientation: " + m_orientation;
        m_log += move;
        m_completedMoves++;
        return move;
    }
    
    public void readSensors()
    {
        this.readLeftSensor();
        this.readRightSensor();
        this.readForwardSensor();
    }
    
    public String moveForward()
    {
        String move = "\nMove Forward: \n\tPrevious Position:("+ m_xPosition + ", " + m_yPosition + ") Previous Orientation: " + m_orientation;
        switch(this.orientation())
        {
            case UP:
                this.moveUp();
                break;
            case UP_RIGHT:
                this.moveUpRight();
                break;
            case RIGHT:
                this.moveRight();
                break;
            case DOWN_RIGHT:
                this.moveDownRight();
                break;
            case DOWN:
                this.moveDown();
                break;
            case DOWN_LEFT:
                this.moveDownLeft();
                break;
            case LEFT:
                this.moveLeft();
                break;
            case UP_LEFT:
                this.moveUpLeft();
                break;
        }
        this.readSensors();
        this.updateBoardPosition();
        move += "\n\tCurrent  Position:("+ m_xPosition + ", " + m_yPosition + ") Current  Orientation: " + m_orientation;
        m_log += move;
        m_completedMoves++;
        return move;
    }
    
    public String moveBackward()
    {
        String move = "\nMove Backward: \n\tPrevious Position:("+ m_xPosition + ", " + m_yPosition + ") Previous Orientation: " + m_orientation;
        switch(this.orientation())
        {
            case UP:
                this.moveDown();
                break;
            case UP_RIGHT:
                this.moveDownLeft();
                break;
            case RIGHT:
                this.moveLeft();
                break;
            case DOWN_RIGHT:
                this.moveUpLeft();
                break;
            case DOWN:
                this.moveUp();
                break;
            case DOWN_LEFT:
                this.moveUpRight();
                break;
            case LEFT:
                this.moveRight();
                break;
            case UP_LEFT:
                this.moveDownRight();
                break;
        }
        this.updateBoardPosition();
        move += "\n\tCurrent  Position:("+ m_xPosition + ", " + m_yPosition + ") Current  Orientation: " + m_orientation;
        m_log += move;
        m_completedMoves++;
        return move;
    }
    
    private void updateBoardPosition()
    {
        m_fullTerrainBoard.updateRoverPosition(this);
        m_discoveredTerrainBoard.updateRoverPosition(this);
    }
    
    public void readLeftSensor()
    {
        switch(this.orientation())
        {
            case UP:
                this.readSensorUpLeft();
                break;
            case UP_RIGHT:
                this.readSensorUp();
                break;
            case RIGHT:
                this.readSensorUpRight();
                break;
            case DOWN_RIGHT:
                this.readSensorRight();
                break;
            case DOWN:
                this.readSensorDownRight();
                break;
            case DOWN_LEFT:
                this.readSensorDown();
                break;
            case LEFT:
                this.readSensorDownLeft();
                break;
            case UP_LEFT:
                this.readSensorLeft();
                break;
        }
    }
    
    public void readRightSensor()
    {
        switch(this.orientation())
        {
            case UP:
                this.readSensorUpRight();
                break;
            case UP_RIGHT:
                this.readSensorRight();
                break;
            case RIGHT:
                this.readSensorDownRight();
                break;
            case DOWN_RIGHT:
                this.readSensorDown();
                break;
            case DOWN:
                this.readSensorDownLeft();
                break;
            case DOWN_LEFT:
                this.readSensorLeft();
                break;
            case LEFT:
                this.readSensorUpLeft();
                break;
            case UP_LEFT:
                this.readSensorUp();
                break;
        }
    }
    
    public void readForwardSensor()
    {
        switch(this.orientation())
        {
            case UP:
                this.readSensorUp();
                break;
            case UP_RIGHT:
                this.readSensorUpRight();
                break;
            case RIGHT:
                this.readSensorRight();
                break;
            case DOWN_RIGHT:
                this.readSensorDownRight();
                break;
            case DOWN:
                this.readSensorDown();
                break;
            case DOWN_LEFT:
                this.readSensorDownLeft();
                break;
            case LEFT:
                this.readSensorLeft();
                break;
            case UP_LEFT:
                this.readSensorUpLeft();
                break;
        }
    }
    
    public void readSensorUp()
    {
        boolean obstacleFound=false;
        int row=m_yPosition;
        int column=m_xPosition;
        while(!obstacleFound && row > 0)
        {
            if(m_fullTerrainBoard.cellState(row, column) == Cell.State.CLEAR)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.CLEAR);
            }
            else if(m_fullTerrainBoard.cellState(row, column) == Cell.State.OBSTACLE)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.OBSTACLE);
                obstacleFound = true;
            }
            row--;
        }
    }
    
    public void readSensorUpRight()
    {
        boolean obstacleFound=false;
        int row=m_yPosition;
        int column=m_xPosition;
        while(!obstacleFound && row > 0 && column < m_fullTerrainBoard.columns())
        {
            if(m_fullTerrainBoard.cellState(row, column) == Cell.State.CLEAR)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.CLEAR);
            }
            else if(m_fullTerrainBoard.cellState(row, column) == Cell.State.OBSTACLE)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.OBSTACLE);
                obstacleFound = true;
            }
            row--;
            column++;
        }
    }
    
    public void readSensorRight()
    {
        boolean obstacleFound=false;
        int row=m_yPosition;
        int column=m_xPosition;
        while(!obstacleFound && column < m_fullTerrainBoard.columns())
        {
            if(m_fullTerrainBoard.cellState(row, column) == Cell.State.CLEAR)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.CLEAR);
            }
            else if(m_fullTerrainBoard.cellState(row, column) == Cell.State.OBSTACLE)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.OBSTACLE);
                obstacleFound = true;
            }
            column++;
        }
    }
    
    public void readSensorDownRight()
    {
        boolean obstacleFound=false;
        int row=m_yPosition;
        int column=m_xPosition;
        while(!obstacleFound && row < m_fullTerrainBoard.rows() && column < m_fullTerrainBoard.columns())
        {
            if(m_fullTerrainBoard.cellState(row, column) == Cell.State.CLEAR)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.CLEAR);
            }
            else if(m_fullTerrainBoard.cellState(row, column) == Cell.State.OBSTACLE)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.OBSTACLE);
                obstacleFound = true;
            }
            row++;
            column++;
        }
    }
    
    public void readSensorDown()
    {
        boolean obstacleFound=false;
        int row=m_yPosition;
        int column=m_xPosition;
        while(!obstacleFound && row < m_fullTerrainBoard.rows())
        {
            if(m_fullTerrainBoard.cellState(row, column) == Cell.State.CLEAR)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.CLEAR);
            }
            else if(m_fullTerrainBoard.cellState(row, column) == Cell.State.OBSTACLE)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.OBSTACLE);
                obstacleFound = true;
            }
            row++;
        }
    }
    
    public void readSensorDownLeft()
    {
        boolean obstacleFound=false;
        int row=m_yPosition;
        int column=m_xPosition;
        while(!obstacleFound && row < m_fullTerrainBoard.rows() && column > 0)
        {
            if(m_fullTerrainBoard.cellState(row, column) == Cell.State.CLEAR)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.CLEAR);
            }
            else if(m_fullTerrainBoard.cellState(row, column) == Cell.State.OBSTACLE)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.OBSTACLE);
                obstacleFound = true;
            }
            row++;
            column--;
        }
    }
    
    public void readSensorLeft()
    {
        boolean obstacleFound=false;
        int row=m_yPosition;
        int column=m_xPosition;
        while(!obstacleFound && column > 0)
        {
            if(m_fullTerrainBoard.cellState(row, column) == Cell.State.CLEAR)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.CLEAR);
            }
            else if(m_fullTerrainBoard.cellState(row, column) == Cell.State.OBSTACLE)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.OBSTACLE);
                obstacleFound = true;
            }
            column--;
        }
    }
    
    public void readSensorUpLeft()
    {
        boolean obstacleFound=false;
        int row=m_yPosition;
        int column=m_xPosition;
        while(!obstacleFound && row > 0 && column > 0)
        {
            if(m_fullTerrainBoard.cellState(row, column) == Cell.State.CLEAR)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.CLEAR);
            }
            else if(m_fullTerrainBoard.cellState(row, column) == Cell.State.OBSTACLE)
            {
                m_discoveredTerrainBoard.setCell(row, column, Cell.State.OBSTACLE);
                obstacleFound = true;
            }
            row--;
            column--;
        }    
    }
    
    public void setup() throws IOException
    {
        this.setupInitialPosition();
        this.setupInitialOrientation();
    }
    
    public void setup(int x, int y, int orientation)
    {
        this.setupInitialPosition(x, y);
        this.setupInitialOrientation(orientation);
    }
    
    private void setupInitialPosition(int x, int y)
    {
        m_xPosition = x;
        m_yPosition = y;
    }
    
    private void setupInitialOrientation(int orientation)
    {
        switch(orientation)
        {
            case 0:
                m_orientation = Orientation.RIGHT;
                break;
            case 1:
                m_orientation = Orientation.UP_RIGHT;
                break;
            case 2:
                m_orientation = Orientation.UP;
                break;
            case 3:
                m_orientation = Orientation.UP_LEFT;
                break;
            case 4:
                m_orientation = Orientation.LEFT;
                break;
            case 5:
                m_orientation = Orientation.DOWN_LEFT;
                break;
            case 6:
                m_orientation = Orientation.DOWN;
                break;
            case 7:
                m_orientation = Orientation.DOWN_RIGHT;
                break;
        }
    }
    
    private void displayOrientationMenu()
    {
        System.out.println("[1]: UP");
        System.out.println("[2]: UP_RIGHT");
        System.out.println("[3]: RIGHT");
        System.out.println("[4]: DOWN_RIGHT");
        System.out.println("[5]: DOWN");
        System.out.println("[6]: DOWN_LEFT");
        System.out.println("[7]: LEFT");
        System.out.println("[8]: UP_LEFT");
    }
    
    private void setupInitialPosition() throws IOException
    {
        System.out.println("Please input the initial row position of the rover:");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        Integer row = new Integer(br.readLine());
        
        System.out.println("Please input the initial column position of the rover:");
        BufferedReader br2 = new BufferedReader(new InputStreamReader(System.in));
        Integer column = new Integer(br2.readLine());
        
        this.position(column.intValue(), row.intValue());
    }
    
    private void setupInitialOrientation() throws IOException
    {
        this.displayOrientationMenu();
        System.out.println("Select initial orientation:");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        Integer orientation = new Integer(br.readLine());
        this.orientation(Orientation.values()[orientation.intValue()]);
    }
    
    public int x()
    {
        return m_xPosition;
    }
    
    public void x(int x)
    {
        m_xPosition = x;
    }
    
    public int y()
    {
        return m_yPosition;
    }
    
    public void y(int y)
    {
        m_yPosition = y;
    }
    
    public void position(int x, int y)
    {
        m_xPosition = x;
        m_yPosition = y;
    }
    
    public void orientation(Orientation orientation)
    {
        m_orientation = orientation;
    }
    
    public Orientation orientation()
    {
        return m_orientation;
    }
    
    public void setFullTerrainBoard(Board board)
    {
        m_fullTerrainBoard = board;
    }
    
    public void setDiscoveredBoard(Board board)
    {
        m_discoveredTerrainBoard = board;
    }
    
    public String toString()
    {
        String retval=" ";
        switch(m_orientation)
        {
            case UP:
                retval = "^";
                break;
            case UP_RIGHT:
                retval = "R";
                break;
            case RIGHT:
                retval = ">";
                break;
            case DOWN_RIGHT:
                retval = "r";
                break;
            case DOWN:
                retval = "V";
                break;
            case DOWN_LEFT:
                retval = "l";
                break;
            case LEFT:
                retval = "<";
                break;
            case UP_LEFT:
                retval = "L";
                break;
        }
        return retval;
    }
}
